package orbito.util;

public class Vec3 {
	public double x, y, z;
	
	public Vec3() {
		this(0.0, 0.0, 0.0);
	}
	
	public Vec3(Vec3 other) {
		this(other.x, other.y, other.z);
	}
	
	public Vec3 (double x, double y, double z) {
		this.x = x;
		this.y = y;
		this.z = z;
	}
	
	public Vec3 add(Vec3 other) {
		return new Vec3(this.x + other.x, this.y + other.y, this.z + other.z);
	}
	
	public Vec3 iadd(Vec3 other) {
		this.x += other.x;
		this.y += other.y;
		this.z += other.z;
		return this;
	}
	
	public Vec3 sub(Vec3 other) {
		return new Vec3(this.x - other.x, this.y - other.y, this.z - other.z);
	}
	
	public Vec3 isub(Vec3 other) {
		this.x -= other.x;
		this.y -= other.y;
		this.z -= other.z;
		return this;
	}
	
	public Vec3 mul(double k) {
		return new Vec3(x * k, y * k, z * k);
	}
	
	public Vec3 imul(double k) {
		x *= k;
		y *= k;
		z *= k;
		return this;
	}
	
	public Vec3 div(double k) {
		double ik = 1 / k;
		return new Vec3(x * ik, y * ik, z * ik);
	}
	
	public Vec3 idiv(double k) {
		double ik = 1 / k;
		x *= ik;
		y *= ik;
		z *= ik;
		return this;
	}
	
	public Vec3 negate() {
		return new Vec3(-x, -y, -z);
	}
	
	public Vec3 inegate() {
		x = -x;
		y = -y;
		z = -z;
		return this;
	}
	
	public Vec3 unit() {
		double il = 1 / length();
		return new Vec3(x * il, y * il, z * il);
	}
	
	public Vec3 iunit() {
		double il = 1 / length();
		x *= il;
		y *= il;
		z *= il;
		return this;
	}
	
	public Vec3 zeroize() {
		x = 0.0;
		y = 0.0;
		z = 0.0;
		return this;
	}
	
	public double length() {
		return Math.sqrt(x * x + y * y);
	}
	
	public double lengthSquared() {
		return x * x + y * y;
	}
	
	/**
	 * Dot product of two vectors.
	 * Source: http://en.wikipedia.org/wiki/Dot_product
	 * 
	 * @param a
	 * @param b
	 * @return The dot product.
	 */
	public static double dot(Vec3 a, Vec3 b) {
		return a.x * b.x + a.y * b.y + a.z * b.z;
	}
	
	/**
	 * Cross product of two vectors.
	 * Source: http://en.wikipedia.org/wiki/Cross_product
	 * 
	 * @param a
	 * @param b
	 * @return The cross product.
	 */
	public static Vec3 cross(Vec3 a, Vec3 b) {
		return new Vec3(
				a.y * b.z - a.z * b.y,
				a.z * b.x - a.x * b.z,
				a.x * b.y - a.y * b.x);
	}
	
	/**
	 * Relative angle between two vectors.
	 * 
	 * @param a
	 * @param b
	 * @return Relative angle between two vectors, in radians.
	 */
	public static double getRelativeAngle(Vec3 a, Vec3 b) {
		return Math.acos(Vec3.dot(a.unit(), b.unit()));
	}
	
	/**
	 * Linear interpolatation between two vectors.
	 * Source: http://en.wikipedia.org/wiki/Linear_interpolation
	 * 
	 * @param a
	 * @param b
	 * @param factor The factor, typically in [0,1].
	 * @return The linearly interpolated vector.
	 */
	public static Vec3 lerp(Vec3 a, Vec3 b, double factor) {
		return new Vec3(
				a.x + (b.x - a.x) * factor,
				a.y + (b.y - a.y) * factor,
				a.z + (b.z - a.z) * factor);
	}
	
	public String toString() {
		return String.format("Vec2(%.2f, %.2f, %.2f)", x, y, z);
	}
}
